<template>
  <div tabindex="1024"
       ref="show-table"
       class="show-table-container"
       :style="showTableContainerStyle"
  >
    <el-table
      ref="table"
      :cell-class-name="tableCellClassName"
      :header-cell-style="headerCellStyle"
      :cell-style="cellStyle"
      :data="tableData"
      :empty-text="$t(emptyText)"
      border
      :fit="fillMode"
      :max-height="height"
      highlight-current-row
      @current-change="handleCurrentChange"
      @header-click="headerClick"
      :style="showTableContainerStyle"
    >
      <el-table-column
        v-if="enableRowTag && tableData.length > 0"
        type="index"
        fixed="left"
        :width="rowTagWidth">
      </el-table-column>
      <template v-for="item in tableHead">
        <el-table-column
          v-if="!hiddenColName.includes(item)"
          :key="item"
          :property="item"
          :label="aliasColName[item] ? aliasColName[item] : item"
          :header-align="headerAlign"
          :width="fillMode ? '' : cellWidth">
          <template #default="scope">
            <template v-if="editable">
              <input
                v-if="isShowInput(scope.row, scope.column, item)"
                class="input-text"
                ref="showTableInput"
                :value="scope.row[item]"
                @blur="exitEditing($event, scope.row, item)"
                @keydown.enter.prevent="changeNextInput"
                type="text"
              >
              <span
                v-else
                :title="scope.row[item]"
                :class="['span-text', {'span-text-empty': !scope.row[item]}]"
                @dblclick="(mouseEvent) => enterEditing(mouseEvent,scope.row, scope.column)"
              >
                {{ scope.row[item] }}
              </span>
            </template>
            <template v-else>
              <span :title="scope.row[item]" class="span-text">{{ scope.row[item] }}</span>
            </template>
          </template>
        </el-table-column>
      </template>
      <el-table-column
        v-if="operationCol"
        fixed="left"
        :label="$t('message.modelLibrary.option')"
        :width="operationWidth"
        align="center">
        <template v-slot="scope">
          <slot name="operation" :operation="{scope,fixedRowCount,fixedOriginData}">
          </slot>
        </template>
      </el-table-column>
    </el-table>
    <div v-if="showOperationBtn" class="add-btn">
      <input ref="fileInput" type="file" @change="fileSelect($event)" accept=".xlsx,.xls" hidden/>
      <el-button v-if="excelImport" type="primary" size="mini" @click="importFile">
        {{ $t('message.Common.importFile') }}
      </el-button>
      <el-button v-if="permitAdd" type="primary" size="mini" @click="addRow" :disabled="disabledAdd">
        {{ $t('message.Common.addRow') }}
      </el-button>
      <el-button v-if="permitRemove" type="danger" size="mini" @click="deleteRow">
        {{ $t('message.Common.deleteRow') }}
      </el-button>
    </div>
  </div>
</template>

<script>

  import {eventBus} from "../../eventbus/eventbus";

  export default {
    name: 'show-table',
    props: {
      //数据源
      datum: Object,
      //表格高度
      height: {type: String, default: '480px'},
      //表格宽度
      width: {type: String, default: '720px'},
      //单元格宽度
      cellWidth: {
        type: String,
        default: '80px',
      },
      //todo: 是否分页
      showTab: {type: Boolean, default: true},
      //表头对齐方式
      headerAlign: {type: String, default: 'center'},
      //是否开启编辑
      editable: {type: Boolean, default: true},
      //todo: 编辑配置 暂时无作用
      editConfig: {
        type: Object,
        default: () => {
        }
      },

      //开启添加按钮
      permitAdd: {type: Boolean, default: true},
      //开启移除按钮
      permitRemove: {type: Boolean, default: true},
      //excel导入开启
      excelImport: {type: Boolean, default: false},
      //上传请求
      uploadRequest: {
        type: Function,
        default: () => {
        }
      },

      //开启左侧数字标签
      enableRowTag: {type: Boolean, default: true},

      //如果想要获取当前表格选中时的项目数据可以使用该参数
      getSelectRow: {
        type: Function,
      },

      //屏蔽列名
      hiddenColName: {
        type: Array,
        default: () => []
      },
      //别名配置 {colName:aliasName,...}
      aliasColName: {
        type: Array,
        default: () => []
      },
      //todo: 开启冻结操作
      enableFreeze: {
        type: Boolean,
        default: false
      },
      //禁用列配置 可以防止某列被编辑
      banEditorColName: {
        type: Array,
        default: () => []
      },
      //填充模式 将列均分填充到表格
      fillMode: {type: Boolean, default: false},
      //移除回调 会传递移除的数据
      removeCallback: {
        type: Function,
      },
      //值改变回调 传递值的行数据以及属性名和新的值
      valueChangeCallback: {
        type: Function,
      },
      isEmptyColNames: {
        type: Array,
        default: () => []
      },
      //todo: 禁止编辑行
      /**
       *
       * 禁用某一行数据
       * {
       *  key:string,
       *  validate:(item):boolean=>{...},
       *  }
       */
      banEditorRow: {type: Array, default: () => []},
      //允许为空值
      permitEmpty: {type: Boolean, default: false},
      //允许表格数据为空
      permitTableDataEmpty: {type: Boolean, default: false},
      cellValidate: {
        type: Function,
      },
      validateField: {
        type: Array,
        default: () => []
      },
      //显示操作按钮
      showOperationBtn: {
        type: Boolean,
        default: false
      },
      operationCol: {
        type: Boolean,
        default: false
      },
      // 更新模式
      updateMode: {
        type: Boolean,
        default: false
      },
      // 差异化模式
      diffMode: {
        type: Boolean,
        default: false
      },
      // 于差异化配合使用, 标识差异化的行数
      diffCount: {
        type: Number,
        default: 0
      },
      // 固定起始数据
      fixedOriginData: {
        type: Boolean,
        default: false
      },
      // 固定行数
      fixedRowCount: {
        type: Number,
        default: 0
      },
      rollbackValue: {
        type: Boolean,
        default: false
      },
      emptyText: {
        type: String,
        default: "message.showTable.noneData"
      }
    },
    data() {
      return {
        oldTableData: [],
        tableData: [],
        tableHead: [],
        selectedRow: null,
        rowIndex: -1,
        columnIndex: -1,
        hasEmpty: false,

        columnFreezeIndex: -1,
        freezeData: [],
        disabledAdd: false,
        isError: false,
        // 索引列宽度
        rowTagWidth: 40,
        // 操作列的宽度
        operationWidth: 100,
        // 计算后的高度
        computedHeight: 0,
      };
    },
    methods: {

      setCurrent(row) {
        this.$refs.table.setCurrentRow(this.tableData[row]);
      },

      tableCellClassName({row, rowIndex, column, columnIndex}) {
        column.columnIndex = columnIndex;
        column.rowIndex = rowIndex;
      },

      // todo: 冻结列
      headerCellStyle({columnIndex}) {
        if (this.columnFreezeIndex === columnIndex && this.enableFreeze) {
          return {backgroundColor: 'var(--bg-color3)', color: '#cccccc'};
        } else {
          return {};
        }
      },

      // todo: 冻结列
      cellStyle({rowIndex, columnIndex}) {
        if (this.tableData.length - rowIndex <= this.diffCount) {
          return {
            backgroundColor: '#77b650'
          };
        }

        if (this.fixedOriginData && rowIndex < this.fixedRowCount) {
          return {backgroundColor: 'var(--bg-color3)', color: '#cccccc'};
        }

        if (this.columnFreezeIndex === columnIndex && this.enableFreeze) {
          return {backgroundColor: 'var(--bg-color6)', color: '#cccccc'};
        } else {
          return {};
        }
      },

      // todo: 冻结列
      headerClick(column) {
        this.columnFreezeIndex = this.columnFreezeIndex === -1 ? column.columnIndex : -1;
      },

      handleCurrentChange(row) {
        this.selectedRow = row;
        if (!!this.getSelectRow) this.getSelectRow(this.tableData.findIndex(item => item === row));
      },

      changeNextInput() {
        let curInput = this.$refs.showTableInput[0];
        curInput.blur();
        if (this.disabledAdd) {
          // 如果为添加模式下 聚焦到下一个输入框
          this.$nextTick(() => {
            let firstInput = this.$refs.showTableInput[0];
            if (firstInput) {
              firstInput?.focus();
            }
          })
        }
      },
      exitEditing(e, row, key) {
        let cellNewValue = e.target.value;

        this.rowIndex = -1;
        this.columnIndex = -1;
        // 校验单条数据
        let result = this.checkoutValue(cellNewValue, key);

        // 在不开启rollback标识的情况下即使不符合校验预期也会将值赋值到对应单元格
        if (result || !this.rollbackValue) {
          row[key] = cellNewValue.trim();
          let index = this.tableData.findIndex(item => item === row);
          if (!!this.valueChangeCallback) this.valueChangeCallback(index, key, cellNewValue);
        }

        this.checkAllData();
      },

      checkoutValue(value, key) {
        if (!this.rollbackValue) {
          this.isError = true;
        }
        //如果在校验属性中
        if (this.validateField.some(fieldName => fieldName === key)) {
          let result = this.validateEdit(value);
          if (!result.validateResult) {
            this.alertMessage(result.message);
            return result.validateResult;
          }
        }
        if (this.isEmptyColNames.includes(key) && value === '') {
          this.alertMessage(`${key}${this.$t("message.showTable.fieldIsNotEmpty")}`);
          return false;
        }
        if (!this.rollbackValue) {
          this.isError = false;
        }
        return true;
      },

      alertMessage(message) {
        eventBus.emit('alert', message, {
          type: 'error',
        });
        if (this.rollbackValue) {
          this.isError = true;
        }
      },

      validateEdit(newValue) {
        let result = !!this.cellValidate ? this.cellValidate(newValue) : {validateResult: true};
        return result;
      },

      enterEditing(mouseEvent, row, column) {
        let parent = mouseEvent.target.parentElement;
        if (this.diffMode && this.tableData.length - column.rowIndex <= this.diffCount) {
          return;
        }
        if (this.fixedOriginData && (column.rowIndex + 2 <= this.fixedRowCount || column.rowIndex === this.tableData.length - 1)) {
          return;
        }
        this.rowIndex = column.rowIndex;
        this.columnIndex = column.columnIndex;
        this.$nextTick(() => {
          let input = parent.querySelector("input");
          input?.focus();
        })
      },

      addRow() {
        let rowObj = {};
        this.tableHead.forEach(item => rowObj[item] = '');
        this.tableData.push(rowObj);
        this.$nextTick(() => {
          let firstInput = this.$refs.showTableInput[0];
          if (firstInput) {
            firstInput?.focus();
          }
        })
        this.$emit("addNewData");
      },

      deleteRow() {
        // 如果为差异化模式，判定删除行是否大于固定行
        if (this.fixedOriginData && (this.rowIndex + 1) <= this.fixedRowCount) {
          return;
        }
        if (this.tableData.length <= 1 && !this.permitTableDataEmpty) {
          eventBus.emit('alert', this.$t('message.modelLibrary.hasData'), {
            type: 'warning',
          });
          return;
        }
        let deleteIndex = this.tableData.findIndex(item => item === this.selectedRow);
        // 如果为正在添加数据，删除的数据为最后一行则会取消警告
        if (deleteIndex === (this.tableData.length - 1) && this.disabledAdd) {
          this.isError = false;
        }
        if (deleteIndex !== -1) {
          if (!!this.removeCallback) this.removeCallback(this.selectedRow, deleteIndex);
          this.tableData.splice(deleteIndex, 1);
          this.$refs.table.setCurrentRow(null);
          this.selectedRow = null;
        }
      },

      importFile() {
        this.$refs.fileInput.click();
      },

      async fileSelect(e) {
        //导入文件
        let file = e.target?.files[0];
        if (file !== null) {
          let importData = await this.uploadRequest(file);
          if (importData?.status === 200) {
            if (importData.data.code === 200) {
              this.datum.length = 0;
              importData.data.data.forEach(item => {
                let row = {};
                item.forEach(cur => {
                  row[cur.name] = cur.expression;
                });
                this.datum.push(row);
              });
            } else {
              eventBus.emit('alert', importData.data.message, {
                type: 'error',
              });
            }
          } else {
            eventBus.emit('alert', this.$t('message.modelLibrary.tableDataAbnormality'), {
              type: "error",
            })
          }
        }
        // 清空上传文件列表
        e.target.value = '';
      },

      //上移一条数据
      moveUp() {
        let rowIndex = this.tableData.findIndex(item => item === this.selectedRow);
        if (rowIndex > 0) {
          let tempData = this.tableData[rowIndex];
          this.$set(this.tableData, rowIndex, this.tableData[rowIndex - 1]);
          this.$set(this.tableData, rowIndex - 1, tempData);
          this.selectedRow = this.tableData[rowIndex - 1];
        }
      },

      //下一一条数据
      moveDown() {
        let rowIndex = this.tableData.findIndex(item => item === this.selectedRow);
        if (rowIndex === -1) return;
        if (rowIndex < this.tableData.length - 1) {
          let tempData = this.tableData[rowIndex];
          this.$set(this.tableData, rowIndex, this.tableData[rowIndex + 1]);
          this.$set(this.tableData, rowIndex + 1, tempData);
          this.selectedRow = this.tableData[rowIndex + 1];
        }
      },
      checkAllData() {
        let validateResultList = [];
        this.tableData.forEach(row => {
          Object.keys(row).forEach((key => {
            validateResultList.push(this.checkoutValue(row[key], key));
          }))
        });

        this.isError = !validateResultList.every(r => r);
        this.$emit("tableStatusChange", this.isError || this.disabledAdd);
      },
    },
    computed: {
      isShowInput() {
        return function (row, column, key) {
          let isSelectedCell = column.rowIndex === this.rowIndex && column.columnIndex === this.columnIndex;
          if (this.columnFreezeIndex === column.columnIndex || this.banEditorColName.includes(key)) return false;
          if (!this.permitEmpty) return isSelectedCell || row[key] === '';
          return isSelectedCell;
        };
      },
      showTableContainerStyle() {
        let count = parseInt(this.cellWidth) * this.tableHead.length + (this.operationCol ? this.operationWidth : 0) + (this.enableRowTag ? this.rowTagWidth : 0) + 1;
        // 优化在属性不足以填充最大宽度时调整列的体验
        let elWidth = this.$refs.table?.bodyWidth;
        let width = this.fillMode || this.tableData.length === 0
          ? this.width
          : elWidth ? elWidth : count + 'px';
        return {
          width: width,
          maxWidth: this.width,
        };
      },
    },
    watch: {
      tableData: {
        handler() {
          if (!this.permitEmpty) {
            for (let item of this.tableData) {
              for (let key in item) {
                if (item[key] === '') {
                  this.disabledAdd = true;
                  this.$emit("tableStatusChange", this.isError || this.disabledAdd)
                  return;
                }
              }
            }
            this.disabledAdd = false;
            this.$emit("tableStatusChange", this.isError || this.disabledAdd)
          }
          this.checkAllData();
        },
        deep: true,
      },

      datum: {
        handler() {
          if (this.updateMode) {
            this.oldTableData = this.datum;
          } else {
            this.tableData = this.datum;
          }
          this.tableHead = this.datum?.length > 0 ? Object.keys(this.datum[0]) : [];
        },
        immediate: true,
      },
    },
  };
</script>

<style scoped>
  /* 表格容器 */
  .show-table-container {
    /* layout */
    max-height: 100%;
    max-width: 100%;
  }

  /* 功能按钮区域 */
  .add-btn {
    /* layout */
    margin: 0.5em 0;

    width: 240px;
  }

  /* 修改ElementUI组件样式 */
  >>> .el-table {
    background-color: var(--bg-color1);
    color: var(--text-color1);
    user-select: none;
    overflow: hidden;
  }

  >>> .el-table__header-wrapper .cell, >>> .el-table__fixed-header-wrapper .cell {
    white-space: nowrap;
  }

  >>> .el-table tr {
    background-color: var(--bg-color1);
  }

  >>> .el-table th {
    padding: 5px 0;
    color: var(--text-color1);
    background-color: var(--bg-color8);
  }

  >>> .el-table td {
    padding: 4px 8px;
    font-size: var(--text-12);
  }

  >>> .el-table__header-wrapper th.gutter {
    display: initial;
  }

  >>> .el-table--border,
  >>> .el-table--border td,
  >>> .el-table--border th,
  >>> .el-table,
  >>> .el-table th.is-leaf,
  >>> .el-table td,
  >>> .el-table__body {
    border-color: var(--border-color5);
  }

  >>> .el-table--border::after, >>> .el-table--group::after, >>> .el-table::before {
    background-color: var(--border-color5);
  }

  >>> .el-table--enable-row-hover .el-table__body tr:hover > td {
    background-color: var(--bg-color3);
  }

  >>> .el-table__body .el-table__row.current-row > td {
    background-color: var(--bg-color3) !important;
  }

  >>> .el-table__body-wrapper::-webkit-scrollbar {
    height: 8px;
    /*width: 8px;*/
  }

  >>> .el-table__body-wrapper .cell, >>> .el-table__fixed-body-wrapper .cell {
    padding: 0 !important;
    display: flex;
    align-items: center;
  }

  >>> .el-table .cell > div {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  >>> .el-table .cell .span-text {
    flex: 1;
    width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  >>> .el-table .cell .span-text-empty {
    padding: 11px 0;
  }

  >>> .el-table .cell .input-text {
    width: 0;
    flex: 1;
    background-color: var(--bg-color1);
    color: var(--text-color1);
    border: none;
    padding: 0;
    outline: none;
    transition: background-color .25s ease;
  }

  >>> .el-table tr.current-row > td .input-text {
    background-color: var(--bg-color3);
  }

  >>> .el-table--enable-row-hover .el-table__body tr:hover > td .input-text {
    background-color: var(--bg-color3);
  }

  >>> .el-table__empty-block {
    width: 100% !important;
  }

  >>> .el-button--text {
    color: var(--text-color1);
  }

  >>> .el-table .cell {
    text-align: center;
  }

  >>> .el-table__fixed-right::before, >>> .el-table__fixed::before {
    height: 0px;
  }

  >>> .el-table__fixed, >>> .el-table__fixed-right {
    height: auto !important;
    bottom: 8px !important;
  }

  >>> .el-table__fixed-body-wrapper table {
    padding-bottom: 8px;
  }
</style>